package com.zou.chat.component;

import com.zou.chat.msg.MsgType;
import com.zou.chat.msg.Sender;
import com.zou.chat.model.FontAndText;
import com.zou.chat.model.PicInfo;

import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import javax.swing.text.BadLocationException;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.*;
import java.awt.event.*;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 聊天窗体类
 *
 * @author
 * @version 0.1
 */
public class ChatFrame extends JFrame implements MouseListener, Runnable {
    private static final long serialVersionUID = 1L;
    public final int F_WIDTH = 550;
    public final int F_HEIGHT = 500;
    /* 左边和右边要显示的界面 */
    public JLabel left = new JLabel();
    JScrollPane jspChat;
    /*聊天内容*/
    private JTextPane jpChat;
    /*要发送的内容*/
    private JTextPane jpMsg;
    JScrollPane jspMsg;
    /* 插入文字样式就靠它了*/
    private StyledDocument docChat = null;
    private StyledDocument docMsg = null;

    private JButton btnSend;
    /*好友的ip*/
    /*private String friendIP;*/
    /*好友接收消息的端口*/
    /*private int friendPort;*/
    /*字体名称;字号大小;文字颜色*/
    private JComboBox fontName = null, fontSize = null, fontColor = null;
    /*插入按钮;清除按钮;插入图片按钮*/
    private JButton b_shake = null, b_pic, b_remove = null;
    private static final Color TIP_COLOR = new Color(255, 255, 225);
    /* 错误信息气泡提示*/
    private CoolToolTip error_tip;
    /*表情框*/
    private PicsJWindow picWindow;
    private List<PicInfo> myPicInfo = new LinkedList<>();
    private List<PicInfo> receivePicInfo = new LinkedList<>();

    public ChatFrame() {
        init();
    }

    /**
     * 初始化窗体
     */
    private void init() {
        setLayout(new BorderLayout());
        /*this.setUndecorated(true);*/
        /*		getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
         */
        setSize(F_WIDTH, F_HEIGHT);
        this.setMinimumSize(new Dimension(F_WIDTH, F_HEIGHT));
        this.getContentPane().setBackground(Color.GRAY);
        setResizable(false);
        setLocationRelativeTo(null);
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(1);
            }

        });
        this.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentResized(ComponentEvent e) {
                ChatFrame.this.picWindow.dispose();
            }

            @Override
            public void componentMoved(ComponentEvent e) {
                ChatFrame.this.picWindow.dispose();
            }

            @Override
            public void componentHidden(ComponentEvent e) {
                ChatFrame.this.picWindow.dispose();
            }

        });
        /*窗体前置*/
        setAlwaysOnTop(true);
        /*聊天信息框*/
        jpChat = new JTextPane();
        jpChat.addMouseListener(this);
        jpChat.setEditable(false);
        jspChat = new JScrollPane(jpChat,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        /*用户聊天信息输入框*/
        jpMsg = new JTextPane();
        jpMsg.addMouseListener(this);
        jspMsg = new JScrollPane(jpMsg,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        jspMsg.setPreferredSize(new Dimension(100, 100));
        error_tip = new CoolToolTip(this, jspMsg, TIP_COLOR, 3, 10);

        /*发送按钮*/
        btnSend = new JButton("发送");
        btnSend.setFocusable(false);
        /*获得JTextPane的Document用于设置字体*/
        docChat = jpChat.getStyledDocument();
        docMsg = jpMsg.getStyledDocument();

        /*添加鼠标事件监听*/
        btnSend.addMouseListener(this);
        /*字体区*/
        JLabel lblSend = new JLabel();
        lblSend.setLayout(new FlowLayout(FlowLayout.RIGHT));
        String[] str_name = {"宋体", "黑体", "Dialog", "Gulim"};
        String[] str_Size = {"12", "14", "18", "22", "30", "40"};
        //String[] str_Style = { "常规", "斜体", "粗体", "粗斜体" };
        String[] str_Color = {"黑色", "红色", "蓝色", "黄色", "绿色"};
        fontName = new JComboBox(str_name);
        fontSize = new JComboBox(str_Size);
        fontColor = new JComboBox(str_Color);
        Box box = Box.createHorizontalBox();
        box.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
        box.add(new JLabel("字体:"));
        box.add(fontName);

        box.add(Box.createHorizontalStrut(3));
        box.add(new JLabel("字号:"));
        box.add(fontSize);
        box.add(Box.createHorizontalStrut(3));
        box.add(new JLabel("颜色:"));
        box.add(fontColor);
        box.add(Box.createHorizontalStrut(3));
        box.add(btnSend);

        JPanel paneLeftSouth = new JPanel();
        paneLeftSouth.setLayout(new BorderLayout());

        b_pic = new JButton("表情");
        b_pic.setFocusable(false);
        b_shake = new JButton("震动");
        b_shake.setFocusable(false);
        b_remove = new JButton("清空");
        b_remove.setFocusable(false);
        picWindow = new PicsJWindow(this);
        b_pic.addMouseListener(this);
        b_remove.addMouseListener(this);
        b_shake.addMouseListener(this);
        Box box_1 = Box.createHorizontalBox();
        box_1.add(b_pic);
        box_1.add(b_shake);
        box_1.add(b_remove);

        paneLeftSouth.add(box_1, BorderLayout.NORTH);//字体、表情、震动
        paneLeftSouth.add(jspMsg, BorderLayout.CENTER);
        paneLeftSouth.add(box, BorderLayout.SOUTH);
        paneLeftSouth.setBackground(Color.CYAN);

        left.setLayout(new BorderLayout());
        left.setOpaque(false);
        left.add(jspChat, BorderLayout.CENTER);
        left.add(paneLeftSouth, BorderLayout.SOUTH);
        add(left, BorderLayout.CENTER);
        new Thread(this).start();
    }

    @Override
    public void mouseClicked(MouseEvent arg0) {
    }

    @Override
    public void mouseEntered(MouseEvent arg0) {
        error_tip.setVisible(false);
    }

    @Override
    public void mouseExited(MouseEvent arg0) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        picWindow.setVisible(false);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if (getY() <= 0) {
            setLocation(getX(), 0);
        }
        if (e.getButton() != 1)
            return;/*不是左键*/

        JComponent source = (JComponent) e.getSource();
        /*鼠标释放时在事件源内,才响应单击事件*/
        if (e.getX() >= 0 && e.getX() <= source.getWidth() && e.getY() >= 0
                && e.getY() <= source.getHeight()) {
            if (source == btnSend) {
                sendMsg();
            } else if (source == this.b_shake) {
                sendShake();
            } else if (source == this.b_pic) {
                picWindow.setVisible(true);
            } else if (source == this.b_remove) {
                jpChat.setText("");
                jpChat.revalidate();
            }
        }
    }

    /**
     * 向好友发送震动
     */
    public void sendShake() {
        String uname = Sender.localIP + ":" + Sender.SendPort;
        if (!Sender.sendUDPMsg(MsgType.SHAKE, uname, Sender.localIP, Sender.SendPort, "shake")) {
            error_tip.setText("发送失败！");
            error_tip.setVisible(true);
        }
        insert("你向 " + uname + " 发送了一个震动！");
    }

    /**
     * 震动三秒以上
     *
     * @param uname
     */
    public void shake(String uname) {
        setExtendedState(Frame.NORMAL);
        setVisible(true);
        insert(uname + " 给你发了一个震动！");
        new Thread() {
            long begin = System.currentTimeMillis();
            long end = System.currentTimeMillis();
            Point p = ChatFrame.this.getLocationOnScreen();

            public void run() {
                int i = 1;
                while ((end - begin) / 1000 < 3) {
                    ChatFrame.this.setLocation(new Point((int) p.getX() - 5 * i, (int) p.getY() + 5 * i));
                    end = System.currentTimeMillis();
                    try {
                        Thread.sleep(5);
                        i = -i;
                        ChatFrame.this.setLocation(p);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    /**
     * 发送消息
     */
    private FontAndText myFont = null;

    public void sendMsg() {
        String message = jpMsg.getText();
        if (message.length() == 0) {
            error_tip.setText("请输入聊天信息！");
            error_tip.setVisible(true);
            return;
        }
        if (message.length() > 100) {
            error_tip.setText("消息最多一百个字符！你要发送的为" + message.length() + "个字符！");
            error_tip.setVisible(true);
            return;
        }

        String uname = Sender.localIP + ":" + Sender.chatPort;
        myFont = getFontAttrib();
        if (Sender.sendUDPMsg(MsgType.CHAT, uname, Sender.localIP, Sender.SendPort,
                myFont.toString())) {
            addMeg(uname);
            this.jpMsg.setText("");
        } else {
            error_tip.setText("发送消息失败！");
            error_tip.setVisible(true);
        }
    }

    /**
     * 追加新消息到聊天窗体
     */
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
    FontAndText dateFont = new FontAndText("", "宋体", 20, Color.BLUE);

    public void addMeg(String uname) {
        String msg = uname + " " + sf.format(new Date());
        dateFont.setText(msg);
        insert(dateFont);
        pos2 = jpChat.getCaretPosition();
        myFont.setText(jpMsg.getText());
        insert(myFont);
        insertPics(false);
    }

    public void addRecMsg(String uname, String message) {
        setExtendedState(Frame.NORMAL);
        //setVisible(true);
        String msg = uname + " " + sf.format(new Date());
        dateFont.setText(msg);
        insert(dateFont);/*时间和用户信息*/
        int index = message.lastIndexOf("*");

        System.out.println("index=" + index);
        /*很重要的，记录下聊天区域要插入聊天消息的开始位置，*/
        pos1 = jpChat.getCaretPosition();
        if (index > 0 && index < message.length() - 1) {/*存在表情信息*/
            FontAndText attr = getReceiveFont(message.substring(0, index));
            insert(attr);
            receivedPicInfo(message.substring(index + 1, message.length()));
            insertPics(true);
        } else {
            FontAndText attr = getReceiveFont(message);
            insert(attr);
        }
    }

    /**
     * 插入图片
     *
     * @param imgIc
     */
    public void insertSendPic(ImageIcon imgIc) {
        //jpMsg.setCaretPosition(docChat.getLength()); // 设置插入位置
        jpMsg.insertIcon(imgIc); // 插入图片
        System.out.print(imgIc.toString());
        //insert(new FontAttrib()); // 这样做可以换行
    }

    /*
     * 重组收到的表情信息串
     */
    public void receivedPicInfo(String picInfos) {
        String[] infos = picInfos.split("[+]");
        for (int i = 0; i < infos.length; i++) {
            String[] tem = infos[i].split("[&]");
            if (tem.length == 2) {
                PicInfo pic = new PicInfo(Integer.parseInt(tem[0]), tem[1]);
                receivePicInfo.add(pic);
            }
        }
    }

    /**
     * 重组发送的表情信息
     *
     * @return 重组后的信息串  格式为   位置|代号+位置|代号+……
     */
    private String buildPicInfo() {
        StringBuilder sb = new StringBuilder("");
        //遍历jtextpane找出所有的图片信息封装成指定格式
        for (int i = 0; i < this.jpMsg.getText().length(); i++) {
            if (docMsg.getCharacterElement(i).getName().equals("icon")) {
                //ChatPic = (ChatPic)
                Icon icon = StyleConstants.getIcon(jpMsg.getStyledDocument().getCharacterElement(i).getAttributes());
                ChatPic cupic = (ChatPic) icon;
                PicInfo picInfo = new PicInfo(i, cupic.getIm() + "");
                myPicInfo.add(picInfo);
                sb.append(i).append("&").append(cupic.getIm()).append("+");
            }
        }
        System.out.println(sb.toString());
        return sb.toString();
        //return null;
    }

    /**
     * 将收到的消息转化为自定义的字体类对象
     *
     * @param message 收到的聊天信息
     * @return 字体类对象
     */
    public FontAndText getReceiveFont(String message) {
        String[] msgs = message.split("[|]");
        String fontName = "";
        int fontSize = 0;
        String[] color;
        String text = message;
        Color fontC = new Color(222, 222, 222);
        if (msgs.length >= 4) {/*这里简单处理，表示存在字体信息*/
            fontName = msgs[0];
            fontSize = Integer.parseInt(msgs[1]);
            color = msgs[2].split("[-]");
            if (color.length == 3) {
                int r = Integer.parseInt(color[0]);
                int g = Integer.parseInt(color[1]);
                int b = Integer.parseInt(color[2]);
                fontC = new Color(r, g, b);
            }
            text = "";
            for (int i = 3; i < msgs.length; i++) {
                text = text + msgs[i];
            }
        }
        FontAndText attr = new FontAndText();

        attr.setName(fontName);
        attr.setSize(fontSize);
        attr.setColor(fontC);

        attr.setText(text);

        System.out.println("getRecivedFont(String message):" + attr.toString());
        return attr;
    }

    /**
     * 插入图片
     *
     * @param isFriend 是否为朋友发过来的消息
     */
    int pos1;
    int pos2;

    private void insertPics(boolean isFriend) {

        if (isFriend) {
            if (this.receivePicInfo.size() <= 0) {
                return;
            } else {
                for (int i = 0; i < receivePicInfo.size(); i++) {
                    PicInfo pic = receivePicInfo.get(i);
                    String fileName;
                    jpChat.setCaretPosition(pos1 + pic.getPos()); /*设置插入位置*/
                    fileName = "qqdefaultface/" + pic.getVal() + ".gif";/*修改图片路径*/
                    jpChat.insertIcon(new ImageIcon(PicsJWindow.class.getResource(fileName))); /*插入图片*/
                    /*					jpChat.updateUI();*/
                }
                receivePicInfo.clear();
            }
        } else {

            if (myPicInfo.size() <= 0) {
                return;
            } else {
                for (int i = 0; i < myPicInfo.size(); i++) {
                    PicInfo pic = myPicInfo.get(i);
                    jpChat.setCaretPosition(pos2 + pic.getPos()); /*设置插入位置*/
                    String fileName;
                    fileName = "qqdefaultface/" + pic.getVal() + ".gif";/*修改图片路径*/
                    jpChat.insertIcon(new ImageIcon(PicsJWindow.class.getResource(fileName))); /*插入图片*/
                    /*jpChat.updateUI();*/
                }
                myPicInfo.clear();
            }
        }
        jpChat.setCaretPosition(docChat.getLength()); /*设置滚动到最下边*/
        //insert(new FontAttrib()); /*这样做可以换行*/
    }

    /**
     * 将带格式的文本插入JTextPane
     *
     * @param attrib
     */
    private void insert(FontAndText attrib) {
        try { // 插入文本
            docChat.insertString(docChat.getLength(), attrib.getText() + "\n",
                    attrib.getAttrSet());
            jpChat.setCaretPosition(docChat.getLength()); // 设置滚动到最下边
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void insert(String text) {
        try { // 插入文本
            docChat.insertString(docChat.getLength(), text + "\n",
                    dateFont.getAttrSet());
            jpChat.setCaretPosition(docChat.getLength()); // 设置插入位置

        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取所需要的文字设置
     *
     * @return FontAttrib
     */
    private FontAndText getFontAttrib() {
        FontAndText att = new FontAndText();
        att.setText(jpMsg.getText() + "*" + buildPicInfo());//文本和表情信息
        att.setName((String) fontName.getSelectedItem());
        att.setSize(Integer.parseInt((String) fontSize.getSelectedItem()));
        String temp_color = (String) fontColor.getSelectedItem();
        if (temp_color.equals("黑色")) {
            att.setColor(new Color(0, 0, 0));
        } else if (temp_color.equals("红色")) {
            att.setColor(new Color(255, 0, 0));
        } else if (temp_color.equals("蓝色")) {
            att.setColor(new Color(0, 0, 255));
        } else if (temp_color.equals("黄色")) {
            att.setColor(new Color(255, 255, 0));
        } else if (temp_color.equals("绿色")) {
            att.setColor(new Color(0, 255, 0));
        }
        return att;
    }

    ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Override
    public void run() {
        DatagramSocket chatSoc = null;
        try {
            chatSoc = new DatagramSocket(Sender.chatPort);
        } catch (SocketException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this, "系统运行出错！");
            System.exit(1);
        }
        while (true) {
            try {
                byte[] bytes = new byte[1024 * 128];
                DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
                chatSoc.receive(dp);
                executorService.execute(() -> {
                    try {
                        receiveMessage(bytes, dp);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                });
            } catch (Exception e) {
                JOptionPane.showMessageDialog(this, "系统运行出错！");
                e.printStackTrace();
            }
        }
    }

    private void receiveMessage(byte[] bytes, DatagramPacket dp) throws UnsupportedEncodingException {
        String recStr = new String(bytes, 0, dp.getLength(), "UTF-8");
        //System.out.println("GroupsPanel recStr = " + recStr);
        String[] strs = recStr.split("[*]");
        int msgType;
        if (strs.length >= 3) {
            msgType = Integer.parseInt(strs[0]);
        } else {
            /*System.out.println("不是好友发过来的聊天信息！");*/
            return;
        }
        String uname = strs[1];

        StringBuilder message = new StringBuilder(strs[2]);
        if (strs.length > 3) {
            for (int i = 3; i < strs.length; i++) {
                message.append("*").append(strs[i]);
            }
        }
        if (msgType == MsgType.CHAT) {
            this.addRecMsg(uname, message.toString());

        } else if (msgType == MsgType.SHAKE) {
            this.shake(uname);
        }
    }

    public static void setUIFont(FontUIResource f) {
	/*	sets the default font for all Swing components.
		ex.setUIFont (new
		javax.swing.plaf.FontUIResource("Serif",Font.ITALIC,12));*/
        Enumeration keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource)
                UIManager.put(key, f);
        }
    }

    public JButton getPicBtn() {
        return b_pic;
    }

    public static void main(String[] args) {
        /*	 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
         */     /*和系统的观感保持一致*/
        setUIFont(new FontUIResource("宋体", Font.PLAIN, 15));
        new ChatFrame().setVisible(true);
    }
}